#Rem
Header: BBOX wrapper. 
#End

Strict
Import mojo
Import agfx

    '// 
	#Rem
	summary: world physic mouse X position
	#End
	
    Global mouseXWorldPhys:Float
	
	#Rem
	summary: world physic mouse Y position
	#End
    Global mouseYWorldPhys:Float
	
	#Rem
	summary: world mouse X position
	#End
    Global mouseXWorld:Float
	
	#Rem
	summary: world mouse Y position
	#End
    Global mouseYWorld:Float

'-----------------------------------------------------------------------------------
' Base physics body
'-----------------------------------------------------------------------------------
#Rem
	summary: Base BBox2D body
#End
Class TBox2DBody

	Private
	
	Field bodyDef:b2BodyDef
	
	Public
	
	#Rem
		summary: user defined body scale
	#End	
	Field scale:Float
	
	#Rem
		summary: generated physic body 
	#End
	Field body:b2Body	
	
	#Rem
		summary: assigned fixture to body
	#End
	Field fixtureDef:b2FixtureDef
	
	#Rem
		summary: constructor
	#End
	Method New()		
		Self.bodyDef = New b2BodyDef()
	End Method
	
	#Rem
		summary: render 
		add this method to app. OnRender() loop
	#End
	Method Render:Void()
		Local sprite:TSprite = TSprite(Self.body.m_userData)
		sprite.position.Set(Self.body.GetPosition().x*100,Self.body.GetPosition().y*100)
		sprite.rotation = RAD2DEG * Self.body.GetAngle()
		sprite.Draw()
	End Method
	
End Class

#Rem
	summary: callback for mouse drag body
#End
Class TestQueryAABBCallback Extends QueryFixtureCallback
    
    Field includeStatic: Bool
    Field mousePVec:b2Vec2
    Field body:b2Body = null
    
	#Rem
		summary: constructor		
	#End
    Method New( includeStatic:Bool, mousePVec:b2Vec2 )
        Self.includeStatic = includeStatic
        Self.mousePVec = mousePVec
    End
        
	#Rem
		summary: Query the world for overlapping shapes.
	#End
    Method Callback : Bool (fixture:b2Fixture)
        Local shape :b2Shape = fixture.GetShape()
        
        If (fixture.GetBody().GetType() <> b2Body.b2_staticBody  Or  includeStatic)
            Local inside :Bool = shape.TestPoint(fixture.GetBody().GetTransform(), mousePVec)
            If (inside)
                body = fixture.GetBody()
                Return False
            End
        End
        
        Return True
    End
    
End

'-----------------------------------------------------------------------------------
' BOX2D wrapper
'-----------------------------------------------------------------------------------
#Rem
	summary: BBOX2D wrapper
#End
Class TBox2DWrapper

	#Rem
		summary: world scale
	#End	
	Field scale:Float
	#Rem
		summary: physic world
	#End
	Field world:b2World
	#Rem
		summary: debug render callback
	#End
	Field dbgDraw:b2DebugDraw = null
	#Rem
		summary: use mouse drag on objects
	#End
	Field mouseDrag:Bool = True
	#Rem
		summary: mouse joint point
	#End
	Field m_mouseJoint:b2MouseJoint
	#Rem
		summary: polygon shape list
	#End
	Field poly_shapes:StringMap<FlashArray<b2Vec2>>

	'-----------------------------------------------------------------------------------
	#Rem
		summary: constructor
	#End
	Method New()		
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create Bbox2D world by:
		gravity:b2Vec2			- set gravity
		sleep:Bool				- sleep time
		scale:Float=100.0		- world scale
		dbg:Bool = False		- enable debugdraw callback
	#End
	Method CreateWorld:Void(gravity:b2Vec2,sleep:Bool,scale:Float=100.0,dbg:Bool = False)
		Self.scale = 1.0/scale
		Self.world = New b2World(gravity,sleep)
		Self.world.SetWarmStarting(True)
		Self.poly_shapes = New StringMap<FlashArray<b2Vec2>>()

		if (dbg) Then
			Self.dbgDraw = New b2DebugDraw()	
		
			Self.dbgDraw.SetDrawScale(scale)
        	Self.dbgDraw.SetFillAlpha(0.3)
        	Self.dbgDraw.SetLineThickness(1.0)
        	'Self.dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit | b2DebugDraw.e_pairBit)
			Self.dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit )
			Self.world.SetDebugDraw(dbgDraw)
		EndIf
		
	End Method
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Add polygon shape to list	
	#End
	Method AddPolygonShapeTemplate:Void(name:String)
		Local vertices:FlashArray<b2Vec2> = CreateFlashArrayFromFile(name+".txt")
		Self.poly_shapes.Set(name,vertices)
	End

	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create static BOX
		name:String		- object name
		x:Float			- X position
		y:Float			- Y postion
		width:Float		- object width
		height:Float	- object height
	#End
	Method CreateStaticBox:TBox2DBody(name:String,x:Float,y:Float,width:Float,height:Float)
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2PolygonShape = New b2PolygonShape()
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		shape.SetAsBox(width*Self.scale*0.5,height*Self.scale*0.5)
		b.body = Self.world.CreateBody(b.bodyDef)
		b.body.CreateFixture2(shape)
		
		Return b
		
	End Method
	
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create static BOX from Sprite
		name:String			- object name
		sprite:TSprite		- sprite
	#End
	Method CreateStaticBox:TBox2DBody(name:String,sprite:TSprite)
		
		Local x:Float		= sprite.position.x
		Local y:Float		= sprite.position.y
		Local width:Float	= sprite.size.x
		Local height:Float	= sprite.size.y
		
		Local b:TBox2DBody = Self.CreateStaticBox(name,x,y,width,height)
		b.body.m_userData = sprite
		
		Return b
		
	End Method
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create Static POLYGON
		name:String		- object name
		x:Float			- X position
		y:Float			- Y postion
		width:Float		- object width
		height:Float	- object height
		poly:Float[]	- vertex list
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateStaticPolygon:TBox2DBody(name:String, x:Float, y:Float, width:Float, height:Float, poly:FlashArray<b2Vec2>)
		
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2PolygonShape = New b2PolygonShape()
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		
		Local polygon:FlashArray<b2Vec2> = New FlashArray<b2Vec2>()
		Local v:b2Vec2
		Local xp:Float
		Local yp:Float

		For v  = EachIn poly
			xp = v.x * width * Self.scale
			yp = v.y * height * Self.scale
			polygon.Push(New b2Vec2(xp,yp))
		Next
		
		shape.SetAsArray(polygon,polygon.Length())
		
		b.body = Self.world.CreateBody(b.bodyDef)
		b.body.CreateFixture2(shape)
	
		Return b
		
	End Method
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create dynamic BOX
		name:String		- object name
		x:Float			- X position
		y:Float			- Y postion
		width:Float		- object width
		height:Float	- object height
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateDynamicBox:TBox2DBody(name:String,x:Float,y:Float,width:Float,height:Float,fixture:b2FixtureDef)
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2PolygonShape = New b2PolygonShape()
		b.bodyDef.type = b2Body.b2_Body		
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		shape.SetAsBox(width*Self.scale*0.5,height*Self.scale*0.5)
		b.body = Self.world.CreateBody(b.bodyDef)		
		fixture.shape = shape
		b.fixtureDef = fixture
		b.body.CreateFixture(fixture)
		Return b
		
	End Method
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create dynamic POLYGON
		name:String		- object name
		x:Float			- X position
		y:Float			- Y postion
		width:Float		- object width
		height:Float	- object height
		poly:Float[]	- vertex list
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateDynamicPolygon:TBox2DBody(name:String, x:Float, y:Float, width:Float, height:Float, poly:FlashArray<b2Vec2>, fixture:b2FixtureDef)
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2PolygonShape = New b2PolygonShape()
		b.bodyDef.type = b2Body.b2_Body		
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		
		Local polygon:FlashArray<b2Vec2> = New FlashArray<b2Vec2>()
		Local v:b2Vec2
		Local xp:Float
		Local yp:Float
		
		For v = EachIn poly
			xp = v.x * width * Self.scale
			yp = v.y * height * Self.scale
			polygon.Push(New b2Vec2(xp,yp))
		Next
		
		shape.SetAsArray(polygon,polygon.Length())
		
		b.body = Self.world.CreateBody(b.bodyDef)		
		fixture.shape = shape
		b.fixtureDef = fixture
		b.body.CreateFixture(fixture)
		Return b
		
	End Method
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create dynamic BOX  from SPRITE
		name:String			- object name
		sprite:TSprite		- sprite
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateDynamicBox:TBox2DBody(name:String,sprite:TSprite,fixture:b2FixtureDef)
		
		Local x:Float		= sprite.position.x
		Local y:Float		= sprite.position.y
		Local width:Float	= sprite.size.x
		Local height:Float	= sprite.size.y
	
		Local b:TBox2DBody = Self.CreateDynamicBox(name,x,y,width,height,fixture)
		b.body.m_userData = sprite
		b.fixtureDef = fixture
		Return b
		
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create static CIRCLE
		name:String			- object name
		x:Float				- X position
		y:Float				- Y position
		radius:Float		- radius
	#End
	Method CreateStaticCircle:TBox2DBody(name:String,x:Float,y:Float,radius:Float)
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2CircleShape = New b2CircleShape()
		shape.m_radius = radius*Self.scale
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		b.body = Self.world.CreateBody(b.bodyDef)
		b.body.CreateFixture2(shape)
		
		Return b
		
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create static CIRCLE from Sprite
		name:String			- object name
		sprite:TSprite		- sprite
		
	#End
	Method CreateStaticCircle:TBox2DBody(name:String,sprite:TSprite)
	
		Local x:Float		= sprite.position.x
		Local y:Float		= sprite.position.y
		Local radius:Float	= sprite.size.x/2.0
		
		Local b:TBox2DBody = Self.CreateStaticCircle(name,x,y,radius)
		b.body.m_userData = sprite
		
		Return b
		
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create dynamic CIRCLE
		name:String			- object name
		x:Float				- X position
		y:Float				- Y position
		radius:Float		- radius
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateDynamicCircle:TBox2DBody(name:String,x:Float,y:Float,radius:Float,fixture:b2FixtureDef)
		
		Local b:TBox2DBody = New TBox2DBody()
		b.scale = Self.scale
		Local shape:b2CircleShape = New b2CircleShape()
		shape.m_radius = radius*Self.scale
		b.bodyDef.type = b2Body.b2_Body
		b.bodyDef.position.Set(x*Self.scale,y*Self.scale)
		b.body = Self.world.CreateBody(b.bodyDef)
		fixture.shape = shape
		b.fixtureDef = fixture
		b.body.CreateFixture(fixture)
		
		Return b
		
	End Method
	
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: Create dynamic CIRCLE from SPRITE
		name:String			- object name
		sprite:TSprite		- sprite
		fixture:b2FixtureDef - fixture definition
	#End
	Method CreateDynamicCircle:TBox2DBody(name:String,sprite:TSprite,fixture:b2FixtureDef)
	
		Local x:Float		= sprite.position.x
		Local y:Float		= sprite.position.y
		Local radius:Float	= sprite.size.x/2.0
	
		Local b:TBox2DBody = Self.CreateDynamicCircle(name,x,y,radius,fixture)
		b.body.m_userData = sprite
		
		Return b
		
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: update physic world add to onUpdate()
	#End
	Method Update:Void()
	
		Self.world.TimeStep(1.0/60.0, 8, 8)
		Self.world.ClearForces()

		If (Self.mouseDrag) Then
			Self.UpdateMouseWorld()
        	Self.MouseDestroy()
        	Self.MouseDrag()
		End
	End Method
	
	
	'-----------------------------------------------------------------------------------
	#Rem
		summary: render physic world add to onUpdate()
		[i]only when you need show debug BBOX2D data[/i]
	#End
	Method Render:Void()
			Self.world.DrawDebugData()
	End Method
	
	
    '-----------------------------------------------------------------------------------
	#Rem
		summary: Update Mouse World	position
	#End
    Method UpdateMouseWorld : void ()
        
        mouseXWorldPhys = (MOUSE.x)*Self.scale
        mouseYWorldPhys = (MOUSE.y)*Self.scale
        mouseXWorld = (MOUSE.x)
        mouseYWorld = (MOUSE.y)
    End

   
    '-----------------------------------------------------------------------------------
	#Rem
		summary: Mouse Drag
	#End
    Method MouseDrag : void ()
        
        '// mouse press
        If (MouseDown()  And  Not(m_mouseJoint))
            Local body :b2Body = GetBodyAtMouse()
            If (body)
                
                Local md :b2MouseJointDef = New b2MouseJointDef()
                md.bodyA = Self.world.GetGroundBody()
                md.bodyB = body
                md.target.Set(mouseXWorldPhys, mouseYWorldPhys)
                md.collideConnected = True
                md.maxForce = 300.0 * body.GetMass()
                m_mouseJoint = b2MouseJoint(Self.world.CreateJoint(md))
                body.SetAwake(True)
            End
        End
        '// mouse release
        If (Not(MouseDown()))
            
            If (m_mouseJoint)
                
                Self.world.DestroyJoint(m_mouseJoint)
                m_mouseJoint = null
            End
        End
        '// mouse move
        If (m_mouseJoint)
            
            Local p2 :b2Vec2 = New b2Vec2(mouseXWorldPhys, mouseYWorldPhys)
            m_mouseJoint.SetTarget(p2)
        End
    End
    
    '-----------------------------------------------------------------------------------
	#Rem
		summary: Mouse Destroy
	#End
    Method MouseDestroy : void ()
        
        '// mouse press
        If (Not(MouseDown()) And KeyHit(68) > 0)
            Local body :b2Body = GetBodyAtMouse(True)
            If (body)
                
                Self.world.DestroyBody(body)
                Return
            End
        End
    End
    
    
    '-----------------------------------------------------------------------------------
    Field mousePVec:b2Vec2 = New b2Vec2()
    #Rem
		summary: get body from mouse position
	#End
    Method GetBodyAtMouse : b2Body (includeStatic:Bool = False)
        
        '// Make a small box.
        mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys)
        Local aabb:b2AABB = New b2AABB()
        
        aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001)
        aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001)
        
        Local callback:TestQueryAABBCallback = New TestQueryAABBCallback(includeStatic,mousePVec)
        Local fixture:b2Fixture
        
        Self.world.QueryAABB(callback, aabb)
        Return callback.body
    End


End Class

#Rem
footer: Example.
[code]
Strict

Import agfx

Global myTmpScreen:TTmpScreen

Class TAgfx_test_app Extends TFramework

	Method OnCreate:Int()
		
		Super.OnCreate()
				
		DISPLAY.SetVirtualSize(480,320)
		Self.fps_On =True
		myTmpScreen = New TTmpScreen		
		myTmpScreen.Prepare()
		
		
		
		Return 0
	End
	

	
End


Class TTmpScreen Extends TScreen

	Field cube2:TBox2DBody
	Field ball1:TBox2DBody
	Field i:Int
	' START when app is created
	Method Start:Void()
		
		IMAGES.Load("box.png","BOX")
		IMAGES.Load("ball_pink.png","BALL")
		
		' create world
		BOX2D.CreateWorld(New b2Vec2(0,10),True,100,True)
		
		' create ground
		Local ground_left:TBox2DBody = BOX2D.CreateStaticBox("GROUND_LEFT",DISPLAY.VSCREEN_HALF_WIDTH,DISPLAY.VSCREEN_HEIGHT-20,DISPLAY.VSCREEN_WIDTH,10)
		ground_left.body.SetAngle(DEG2RAD*-45.0)
		
		Local ground_right:TBox2DBody = BOX2D.CreateStaticBox("GROUND_RIGHT",DISPLAY.VSCREEN_HALF_WIDTH,DISPLAY.VSCREEN_HEIGHT-20,DISPLAY.VSCREEN_WIDTH,10)
		ground_right.body.SetAngle(DEG2RAD*45.0)
		
		
		Local ground_oval:TBox2DBody = BOX2D.CreateStaticCircle("GROUND_OVAL",DISPLAY.VSCREEN_HALF_WIDTH,DISPLAY.SCREEN_HEIGHT-250,32)
		
		' create dynamic box
		
		Local fixt:b2FixtureDef = New b2FixtureDef()
		fixt.density = 1.0
		fixt.friction = 0.3
		
		Local cube:TBox2DBody = BOX2D.CreateDynamicBox("BOX",320-32,10,32,64,fixt)
		cube.body.SetAngularVelocity(5)
		
		
		' create dynamic circle
		
		Local fixt_ball:b2FixtureDef = New b2FixtureDef()
		fixt_ball.density = 2.0
		fixt_ball.restitution = 0.2
		fixt_ball.friction = 0.5
		
		Local ball:TBox2DBody = BOX2D.CreateDynamicCircle("CIRCLE",320+32,10,16,fixt)
		
		' create dynamic cube from Sprite
		Local spr:TSprite = New TSprite("spr_box",IMAGES.Find("BOX"),320+64,10)
		Self.cube2 = BOX2D.CreateDynamicBox("IMG_BOX",spr,fixt)
		
		'create dynamic circle from sprite
		Local spr_ball:TSprite = New TSprite("spr_ball",IMAGES.Find("BALL"),320-64,10)
		Self.ball1 = BOX2D.CreateDynamicCircle("PHX_BALL",spr_ball,fixt_ball)
		
		
	End
	
	' main app logic loop update
	Method Update:Void()
		
		
		if MOUSE.Hit() Then			
			Local fixt_b:b2FixtureDef = New b2FixtureDef()
			fixt_b.density = 2.0
			fixt_b.restitution = 0.2
			fixt_b.friction = 0.5
			BOX2D.CreateDynamicCircle("CIRCLE",MOUSE.x,MOUSE.y,16,fixt_b)
		EndIf
		
		BOX2D.Update()
	End
	
	' main app render loop
	Method Render:Void()
		Cls
		BOX2D.Render()
		Self.cube2.Render()
		Self.ball1.Render()
	End
End
 
[/code]
#End